﻿using System;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Text;
using System.Xml;
using System.Security;

namespace Syfuhs.APIWrappers.LiveMeeting
{
    /// <summary>
    /// Live Meeting API Handler class
    /// </summary>
    [CLSCompliant(true)]
    public class LiveMeetingApiHandler
    {
        private string AuthWrapper;

        /// <summary>
        /// Name of the Live Meeting Conference Center
        /// </summary>
        public string ConferenceCenter { get; set; }

        /// <summary>
        /// The URL for the Conference Center
        /// </summary>
        public Uri ConferenceCenterUrl { get; set; }

        /// <summary>
        /// The Live Meeting Conference API URL
        /// </summary>
        public Uri PostingUrl { get; set; }

        //public object test(XmlDocument doc)
        //{
        //    return UploadFile("testName", "testFileName", new byte[1024], ResourceType.ESS);
        //}

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="conferenceCenter">Name of the conference center to handle</param>
        /// <param name="ApiUser">Username for API Access</param>
        /// <param name="ApiPassword">Password associated to APIUser</param>
        public LiveMeetingApiHandler(string conferenceCenter, string ApiUser, string ApiPassword)
        {
            Contract.Requires(!string.IsNullOrEmpty(conferenceCenter), Exceptions.ExceptionResources.ConferenceCenterNull);
            Contract.Requires(!string.IsNullOrEmpty(ApiUser), Exceptions.ExceptionResources.ApiUserNull);
            Contract.Requires(!string.IsNullOrEmpty(ApiPassword), Exceptions.ExceptionResources.ApiPasswordNull);

            this.ConferenceCenter = conferenceCenter;
            this.PostingUrl = HttpRequestHelper.GetPostingUrlRequest(conferenceCenter);

            this.ConferenceCenterUrl = new Uri(string.Format(CultureInfo.InvariantCulture, "http://www.livemeeting.com/cc/{0}", conferenceCenter));

            this.AuthWrapper = CreateAuthWrapper(ApiUser, ApiPassword);
        }

        /// <summary>
        /// Gets the Conference Center's customizable URLs
        /// </summary>
        /// <returns>Returns Customized links structure</returns>
        public CustomizedLinks RetrieveCustomizedLinks()
        {
            CustomizedLinks links = new CustomizedLinks();
            Uri url = new Uri(string.Format(CultureInfo.InvariantCulture, "{0}/GetCustomizedLinksRequest", PostingUrl));

            XmlNodeList nodes
                = (HttpRequestHelper.GetXmlMessageRequest(url) as XmlDocument)
                .SelectNodes("PlaceWareConfCenter/GetCustomizedLinksReply/OptionList/StringOption");

            for (int i = 0; i < nodes.Count; i++)
                links.GetType()
                    .GetProperty(nodes[i].SelectSingleNode("@name").Value.CapitalizeFirstCharacter())
                    .SetValue(links, new Uri(nodes[i].SelectSingleNode("@value").Value), null);

            return links;
        }

        /// <summary>
        /// Get the login Url to use the ticket
        /// </summary>
        /// <param name="ticket">The ticket</param>
        /// <returns>Returns Url to log in</returns>
        public Uri GetSecurityContextTicketUrl(string ticket)
        {
            return new Uri(string.Format(CultureInfo.InvariantCulture, "{0}?ticket={1}", this.ConferenceCenterUrl, ticket));
        }

        /// <summary>
        /// Get a security context ticket for another user without explicitly knowing their password
        /// </summary>
        /// <param name="user">Username to get ticket</param>
        /// <param name="duration">Length of time ticket is valid</param>
        /// <returns>Returns authorization ticket to run as another user</returns>
        public string GetUserSecurityContextTicket(string user, int duration)
        {
            Contract.Requires(!string.IsNullOrEmpty(user), Exceptions.ExceptionResources.UserNameNull);
            Contract.Requires(duration > 0, Exceptions.ExceptionResources.TicketDurationLessThanOne);

            return ExtractSecurityContextTicket((HttpRequestHelper.PostXmlMessageRequest(GetSecurityContextTicketHelper(user, duration), PostingUrl) as XmlDocument));
        }

        /// <summary>
        /// Create a group with given users
        /// </summary>
        /// <param name="groupName">Name of group</param>
        /// <param name="users">Collection of users to add to group</param>
        /// <returns>Returns true if completed successfully</returns>
        public bool CreateGroup(string groupName, string[] users)
        {
            Contract.Requires(!string.IsNullOrEmpty(groupName), Exceptions.ExceptionResources.GroupNameNull);
            Contract.Requires(users != null && users.Length > 0, Exceptions.ExceptionResources.GroupUsersLessThanOne);

            return HttpRequestHelper.PostXmlMessageRequest(CreateGroupHelper(groupName, users), PostingUrl) != null;
        }

        /// <summary>
        /// Delete group
        /// </summary>
        /// <param name="groupName">Name of group to delete</param>
        /// <returns>Returns true if group deleted successfully</returns>
        public bool DeleteGroup(string groupName)
        {
            Contract.Requires(!string.IsNullOrEmpty(groupName), Exceptions.ExceptionResources.GroupNameNull);

            return HttpRequestHelper.PostXmlMessageRequest(DeleteGroupHelper(groupName), PostingUrl) != null;
        }

        /// <summary>
        /// Get a specific group
        /// </summary>
        /// <param name="groupName">Name of the group</param>
        /// <returns>Returns the group</returns>
        public LiveMeetingGroup GetGroup(string groupName)
        {
            Contract.Requires(!string.IsNullOrEmpty(groupName));

            return ParseGroupResponse(HttpRequestHelper.PostXmlMessageRequest(GetGroupHelper(groupName), PostingUrl) as XmlDocument);
        }

        /// <summary>
        /// Delete a meeting
        /// </summary>
        /// <param name="mid">Meeting ID</param>
        /// <returns>Returns true of deleted successfully</returns>
        public bool DeleteMeeting(string mid)
        {
            Contract.Requires(!string.IsNullOrEmpty(mid), Exceptions.ExceptionResources.MeetingIDNull);

            return HttpRequestHelper.PostXmlMessageRequest(DeleteMeetingHelper(mid), PostingUrl) != null;
        }

        /// <summary>
        /// Get all the groups belonging to this conference center
        /// </summary>
        /// <returns>Returns an array of groups</returns>
        public LiveMeetingGroup[] GetGroups()
        {
            return ParseGroupsResponse(HttpRequestHelper.PostXmlMessageRequest(GetGroupsHelper(), PostingUrl) as XmlDocument);
        }

        /// <summary>
        /// Get user object based on email
        /// </summary>
        /// <param name="user">Email of user wanted to get</param>
        /// <returns>Returns Live Meeting User</returns>
        public LiveMeetingUser GetUser(string user)
        {
            Contract.Requires(!string.IsNullOrEmpty(user), Exceptions.ExceptionResources.UserNameNull);

            return ExtractUserDetails((HttpRequestHelper.PostXmlMessageRequest(GetUserHelper(user), PostingUrl) as XmlDocument));
        }

        /// <summary>
        /// Get all users in conference center
        /// </summary>
        /// <returns>Returns Array of users</returns>
        public LiveMeetingUser[] GetUsers()
        {
            return ExtractUserList((HttpRequestHelper.PostXmlMessageRequest(GetUsersHelper(), PostingUrl) as XmlDocument));
        }

        /// <summary>
        /// Delete a user from the conference center
        /// </summary>
        /// <param name="userName">Username to delete</param>
        /// <returns>Returns true if deleted</returns>
        public bool DeleteUser(string userName)
        {
            Contract.Requires(!string.IsNullOrEmpty(userName), Exceptions.ExceptionResources.UserNameNull);

            return HttpRequestHelper.PostXmlMessageRequest(DeleteUserHelper(userName), PostingUrl) != null;
        }

        /// <summary>
        /// Get conference center data
        /// </summary>
        /// <returns>Returns Live Meeting Conference Center object</returns>
        public LiveMeetingConferenceCenter GetConferenceCenterData()
        {
            return ConferenceCenterResponseParser.Parse(HttpRequestHelper.PostXmlMessageRequest(RetrieveConferenceCenterDataHelper(), PostingUrl) as XmlDocument);
        }

        /// <summary>
        /// Get a list of meetings within a certain date range
        /// </summary>
        /// <param name="from">Search From</param>
        /// <param name="to">Search To</param>
        /// <param name="listDeleted">List deleted meetings too?</param>
        /// <returns>Returns array of Meeting objects</returns>
        public Meeting[] GetMeetings(DateTime from, DateTime to, bool listDeleted)
        {
            Contract.Requires(from < to, Exceptions.ExceptionResources.MeetingStartsAfterEnds);

            return ParseMeetingCollectionResponse(HttpRequestHelper.PostXmlMessageRequest(GetMeetingsHelper(from, to, listDeleted), PostingUrl) as XmlDocument);
        }

        /// <summary>
        /// Get a specific meeting by name
        /// </summary>
        /// <param name="field">Searching for</param>
        /// <param name="useMid">True searches on mid otherwise searches by name</param>
        /// <returns>Returns Meeting object</returns>
        public Meeting GetMeeting(string field, bool useMid)
        {
            Contract.Requires(!string.IsNullOrEmpty(field), Exceptions.ExceptionResources.SearchFieldNull);

            return ParseMeetingResponse((HttpRequestHelper.PostXmlMessageRequest(GetMeetingHelper(field, useMid), PostingUrl) as XmlDocument));
        }

        /// <summary>
        /// Create a new meeting
        /// </summary>
        /// <param name="meetingName">Unique name for meeting</param>
        /// <param name="meetingTitle">Title fo meeting</param>
        /// <param name="meetingStartTime">Meeting start time</param>
        /// <param name="meetingEndTime">Meeting end time</param>
        /// <returns>Returns meeting object</returns>
        public Meeting CreateNewMeeting(string meetingName, string meetingTitle, DateTime meetingStartTime, DateTime meetingEndTime, int maxUsers)
        {
            return CreateNewMeeting(meetingName, meetingTitle, meetingStartTime, meetingEndTime, new string[0], maxUsers);
        }

        /// <summary>
        /// Create a new meeting
        /// </summary>
        /// <param name="meetingName">Unique name for meeting</param>
        /// <param name="meetingTitle">Title fo meeting</param>
        /// <param name="meetingStartTime">Meeting start time</param>
        /// <param name="meetingEndTime">Meeting end time</param>
        /// <param name="audienceMembers">List of audience members</param>
        /// <returns>Returns Meeting object</returns>
        public Meeting CreateNewMeeting(string meetingName, string meetingTitle, DateTime meetingStartTime, DateTime meetingEndTime, string[] audienceMembers, int maxUsers)
        {
            Contract.Requires(!string.IsNullOrEmpty(meetingName), Exceptions.ExceptionResources.MeetingNameNull);
            Contract.Requires(!string.IsNullOrEmpty(meetingTitle), Exceptions.ExceptionResources.MeetingTitleNull);
            Contract.Requires(meetingStartTime < meetingEndTime, Exceptions.ExceptionResources.MeetingStartsAfterEnds);
            Contract.Requires(maxUsers > 0, Exceptions.ExceptionResources.MaxUsersLessThanOne);

            HttpRequestHelper.PostXmlMessageRequest(CreateMeetingHelper(meetingName, meetingTitle, meetingStartTime, meetingEndTime, true, audienceMembers, maxUsers), PostingUrl);

            return GetMeeting(meetingName, false);
        }

        /// <summary>
        /// Update a meeting with new details
        /// </summary>
        /// <param name="updatedMeeting">The unsaved updated meeting</param>
        /// <returns>The saved updated meeting</returns>
        public Meeting UpdateMeeting(Meeting updatedMeeting)
        {
            Contract.Requires(updatedMeeting != null, Exceptions.ExceptionResources.MeetingNull);

            // get original meeting
            // check each property for change
            //      - add item to OptionList
            // send update
            // return new meeting
            HttpRequestHelper.PostXmlMessageRequest(BuildMeetingUpdateRequest(this.GetMeeting(updatedMeeting.Mid, true), updatedMeeting), PostingUrl);

            return GetMeeting(updatedMeeting.Mid, true);
        }

        /// <summary>
        /// Create a new user
        /// </summary>
        /// <param name="userName">The username requested</param>
        /// <param name="firstName">The user's first name</param>
        /// <param name="lastName">The user's last name</param>
        /// <param name="email">The user's email address</param>
        /// <returns>Returns a user object</returns>
        public LiveMeetingUser CreateNewUser(string userName, string firstName, string lastName, string email, bool sendWelcomeMessage)
        {
            return CreateNewUser(userName, string.Empty, firstName, lastName, email, sendWelcomeMessage);
        }

        /// <summary>
        /// Create a new user
        /// </summary>
        /// <param name="username">The username requested</param>
        /// <param name="password">The user's requested password</param>
        /// <param name="firstName">The user's first name</param>
        /// <param name="lastName">The user's last name</param>
        /// <param name="email">The user's email address</param>
        /// <returns>Returns a user object</returns>
        public LiveMeetingUser CreateNewUser(string userName, string password, string firstName, string lastName, string email, bool sendWelcomeMessage)
        {
            Contract.Requires(!string.IsNullOrEmpty(userName), Exceptions.ExceptionResources.UserNameNull);
            Contract.Requires(!string.IsNullOrEmpty(email), Exceptions.ExceptionResources.UserEmailNull);

            return CreateNewUserhelper(userName, password, firstName, lastName, email, sendWelcomeMessage);
        }

        /// <summary>
        /// Upload a file to store with a specific meeting
        /// </summary>
        /// <param name="meetingName">Name of the meeting</param>
        /// <param name="fileName">Name of the file</param>
        /// <param name="payload">The file</param>
        /// <param name="type">Type of file</param>
        /// <returns>Returns true if successful upload</returns>
        public bool UploadFile(string meetingName, string fileName, byte[] payload, ResourceType type)
        {
            Contract.Requires(!string.IsNullOrEmpty(meetingName), Exceptions.ExceptionResources.MeetingNameNull);
            Contract.Requires(!string.IsNullOrEmpty(fileName), Exceptions.ExceptionResources.FileNameNull);
            Contract.Requires(payload != null && payload.Length > 0, Exceptions.ExceptionResources.PayloadNull);

            return parseUploadResponse(PostUploadRequest(meetingName, fileName, payload.Length, type), payload);
        }

        private LiveMeetingGroup ParseGroupResponse(XmlDocument response)
        {
            XmlNode groupNode = response.SelectSingleNode("PlaceWareConfCenter/GetGroupReply/Group");
            LiveMeetingUserCollection users = new LiveMeetingUserCollection();

            foreach (XmlNode node in groupNode.SelectNodes("UserList"))
                foreach (XmlNode sub in node.SelectNodes("U"))
                    users.Add(GetUser(sub.InnerText));

            return new LiveMeetingGroup() { GroupMembers = users, GroupName = groupNode.SelectSingleNode("@groupID").InnerText };
        }

        private XmlDocument GetGroupHelper(string groupName)
        {
            Contract.Requires(!string.IsNullOrEmpty(AuthWrapper));

            return ToXmlDocument(string.Format(CultureInfo.InvariantCulture, AuthWrapper, string.Format(CultureInfo.InvariantCulture, "<GetGroupRequest groupID=\"{0}\"/>", groupName)));
        }

        private LiveMeetingGroup[] ParseGroupsResponse(XmlDocument response)
        {
            XmlNode groupNode = response.SelectSingleNode("PlaceWareConfCenter");
            LiveMeetingGroupCollection groups = new LiveMeetingGroupCollection();

            foreach (XmlNode node in groupNode.SelectNodes("ListGroupsReply"))
                foreach (XmlNode sub in node.SelectNodes("GroupID"))
                    groups.Add(GetGroup(sub.InnerText));

            return groups.ToArray();
        }

        private XmlDocument GetGroupsHelper()
        {
            return ToXmlDocument(string.Format(CultureInfo.InvariantCulture, AuthWrapper, string.Format(CultureInfo.InvariantCulture, "<ListGroupsRequest/>")));
        }

        private XmlDocument DeleteMeetingHelper(string mid)
        {
            Contract.Requires(!string.IsNullOrEmpty(this.AuthWrapper));

            return ToXmlDocument(string.Format(CultureInfo.InvariantCulture, AuthWrapper,
                string.Format(CultureInfo.InvariantCulture, "<DeleteMeetingRequest><StringQuery fieldName=\"mid\" operator=\"=\" value=\"{0}\"/></DeleteMeetingRequest>", mid)));
        }

        private static LiveMeetingUser ExtractUserDetails(XmlDocument response)
        {
            LiveMeetingUser user = new LiveMeetingUser();

            user.UserName = response.SelectSingleNode("PlaceWareConfCenter/GetUser7Reply/User").Attributes["userID"].Value;

            user.SetSettings(UserResponseParser.Parse(response));
            user.EmailAddress = user.Settings.Email;
            user.FirstName = user.Settings.FirstName;
            user.LastName = user.Settings.LastName;

            return user;
        }

        private XmlDocument GetUserHelper(string user)
        {
            Contract.Requires(!string.IsNullOrEmpty(this.AuthWrapper));

            return ToXmlDocument(string.Format(CultureInfo.InvariantCulture, AuthWrapper, string.Format(CultureInfo.InvariantCulture, "<GetUser7Request userID=\"{0}\"/>", user)));
        }

        private XmlDocument GetUsersHelper()
        {
            Contract.Requires(!string.IsNullOrEmpty(this.AuthWrapper));

            return ToXmlDocument(string.Format(CultureInfo.InvariantCulture, AuthWrapper, string.Format(CultureInfo.InvariantCulture, "<ListUsersRequest/>")));
        }

        private LiveMeetingUser[] ExtractUserList(XmlDocument response)
        {
            LiveMeetingUserCollection users = new LiveMeetingUserCollection();

            foreach (XmlNode node in response.SelectSingleNode("PlaceWareConfCenter").SelectNodes("ListUsersReply"))
                foreach (XmlNode sub in node.SelectNodes("U"))
                    users.Add(GetUser(sub.InnerText));

            return users.ToArray();
        }

        /// <summary>
        /// Delete a user from the conference center
        /// </summary>
        /// <param name="userName">Username to delete</param>
        /// <returns>Returns XML string request</returns>
        private XmlDocument DeleteUserHelper(string userName)
        {
            Contract.Requires(!string.IsNullOrEmpty(this.AuthWrapper));

            return ToXmlDocument(string.Format(CultureInfo.InvariantCulture, AuthWrapper, string.Format(CultureInfo.InvariantCulture, "<DeleteUserRequest userID=\"{0}\"/>", userName)));
        }

        /// <summary>
        /// Get's conference center data
        /// </summary>
        /// <returns>Returns XML string request</returns>
        private XmlDocument RetrieveConferenceCenterDataHelper()
        {
            Contract.Requires(!string.IsNullOrEmpty(this.AuthWrapper));

            return ToXmlDocument(string.Format(CultureInfo.InvariantCulture, AuthWrapper, "<GetConferencingServiceData7Request roleName=\"Administrator\"/>"));
        }

        /// <summary>
        /// Parses a meeting search response
        /// </summary>
        /// <param name="response">The Response</param>
        /// <returns>Returns a collection of Meetings</returns>
        private static Meeting[] ParseMeetingCollectionResponse(XmlDocument response)
        {
            return MeetingResponseParser.ParseCollection(response);
        }

        /// <summary>
        /// Parses a meeting search response
        /// </summary>
        /// <param name="response">The Response</param>
        /// <returns>Returns a collection of Meetings</returns>
        private static Meeting ParseMeetingResponse(XmlDocument response)
        {
            return MeetingResponseParser.Parse(response);
        }

        /// <summary>
        /// Build the meeting request
        /// </summary>
        /// <param name="from">Search from</param>
        /// <param name="to">Search to</param>
        /// <param name="listDeleted">Include deleted meetings?</param>
        /// <returns></returns>
        private XmlDocument GetMeetingsHelper(DateTime from, DateTime to, bool listDeleted)
        {
            StringBuilder request = new StringBuilder();

            request.AppendFormat(CultureInfo.InvariantCulture, "<ListMeetingsRequest listDeleted=\"{0}\">", listDeleted);
            request.AppendFormat(CultureInfo.InvariantCulture, "<TimeIntervalQuery fieldName=\"startTime\">");
            request.AppendFormat(CultureInfo.InvariantCulture, "<TimeInterval startTime=\"{0}\" endTime=\"{1}\"/>", ToUTCZulu(from), ToUTCZulu(to));
            request.AppendFormat(CultureInfo.InvariantCulture, "</TimeIntervalQuery>");
            request.AppendFormat(CultureInfo.InvariantCulture, "<FieldList>");
            request.AppendFormat(CultureInfo.InvariantCulture, "<Name>applicationSharingNow</Name>");
            request.AppendFormat(CultureInfo.InvariantCulture, "<Name>audienceInviteText</Name>");
            request.AppendFormat(CultureInfo.InvariantCulture, "<Name>createTime</Name>");
            request.AppendFormat(CultureInfo.InvariantCulture, "<Name>easyAssistJoinLink</Name>");
            request.AppendFormat(CultureInfo.InvariantCulture, "<Name>isOngoing</Name>");
            request.AppendFormat(CultureInfo.InvariantCulture, "<Name>mid</Name>");
            request.AppendFormat(CultureInfo.InvariantCulture, "<Name>modifyTime</Name>");
            request.AppendFormat(CultureInfo.InvariantCulture, "<Name>owner</Name>");
            request.AppendFormat(CultureInfo.InvariantCulture, "<Name>presenterInviteText</Name>");
            request.AppendFormat(CultureInfo.InvariantCulture, "<Name>removeTime</Name>");
            request.AppendFormat(CultureInfo.InvariantCulture, "<Name>serverGeneratedAudiencePassword</Name>");
            request.AppendFormat(CultureInfo.InvariantCulture, "<Name>serverGeneratedPresenterPassword</Name>");
            request.AppendFormat(CultureInfo.InvariantCulture, "<Name>status</Name>");
            request.AppendFormat(CultureInfo.InvariantCulture, "<Name>startTime</Name>");
            request.AppendFormat(CultureInfo.InvariantCulture, "<Name>endTime</Name>");
            request.AppendFormat(CultureInfo.InvariantCulture, "<Name>name</Name>");
            request.AppendFormat(CultureInfo.InvariantCulture, "<Name>title</Name>");
            request.AppendFormat(CultureInfo.InvariantCulture, "<Name>audiencePassword</Name>");
            request.AppendFormat(CultureInfo.InvariantCulture, "<Name>presenterPassword</Name>");
            request.AppendFormat(CultureInfo.InvariantCulture, "</FieldList>");
            request.AppendFormat(CultureInfo.InvariantCulture, "</ListMeetingsRequest>");

            return ToXmlDocument(string.Format(CultureInfo.InvariantCulture, AuthWrapper, request.ToString()));
        }

        /// <summary>
        /// Build the meeting search request
        /// </summary>
        /// <param name="field"></param>
        /// <param name="useMid"></param>
        /// <returns></returns>
        private XmlDocument GetMeetingHelper(string field, bool useMid)
        {
            StringBuilder request = new StringBuilder();

            request.AppendFormat(CultureInfo.InvariantCulture, "<ListMeetingsRequest listDeleted=\"{0}\">", true);
            request.AppendFormat(CultureInfo.InvariantCulture, "<StringQuery fieldName=\"{0}\" operator=\"=\" value=\"{1}\"/>", useMid ? "mid" : "name", field);
            request.AppendFormat(CultureInfo.InvariantCulture, "<FieldList>");
            request.AppendFormat(CultureInfo.InvariantCulture, "<Name>applicationSharingNow</Name>");
            request.AppendFormat(CultureInfo.InvariantCulture, "<Name>audienceInviteText</Name>");
            request.AppendFormat(CultureInfo.InvariantCulture, "<Name>createTime</Name>");
            request.AppendFormat(CultureInfo.InvariantCulture, "<Name>easyAssistJoinLink</Name>");
            request.AppendFormat(CultureInfo.InvariantCulture, "<Name>isOngoing</Name>");
            request.AppendFormat(CultureInfo.InvariantCulture, "<Name>mid</Name>");
            request.AppendFormat(CultureInfo.InvariantCulture, "<Name>modifyTime</Name>");
            request.AppendFormat(CultureInfo.InvariantCulture, "<Name>owner</Name>");
            request.AppendFormat(CultureInfo.InvariantCulture, "<Name>presenterInviteText</Name>");
            request.AppendFormat(CultureInfo.InvariantCulture, "<Name>removeTime</Name>");
            request.AppendFormat(CultureInfo.InvariantCulture, "<Name>serverGeneratedAudiencePassword</Name>");
            request.AppendFormat(CultureInfo.InvariantCulture, "<Name>serverGeneratedPresenterPassword</Name>");
            request.AppendFormat(CultureInfo.InvariantCulture, "<Name>status</Name>");
            request.AppendFormat(CultureInfo.InvariantCulture, "<Name>startTime</Name>");
            request.AppendFormat(CultureInfo.InvariantCulture, "<Name>endTime</Name>");
            request.AppendFormat(CultureInfo.InvariantCulture, "<Name>name</Name>");
            request.AppendFormat(CultureInfo.InvariantCulture, "<Name>title</Name>");
            request.AppendFormat(CultureInfo.InvariantCulture, "<Name>audiencePassword</Name>");
            request.AppendFormat(CultureInfo.InvariantCulture, "<Name>presenterPassword</Name>");
            request.AppendFormat(CultureInfo.InvariantCulture, "<Name>maxUsers</Name>");
            request.AppendFormat(CultureInfo.InvariantCulture, "</FieldList>");
            request.AppendFormat(CultureInfo.InvariantCulture, "</ListMeetingsRequest>");

            return ToXmlDocument(string.Format(CultureInfo.InvariantCulture, AuthWrapper, request.ToString()));
        }

        /// <summary>
        /// Build the meeting request
        /// </summary>
        /// <param name="orig">The original meeting</param>
        /// <param name="updated">The updated meeting</param>
        /// <returns>Returns the Xml Request</returns>
        private XmlDocument BuildMeetingUpdateRequest(Meeting orig, Meeting updated)
        {
            StringBuilder sb = new StringBuilder();

            sb.AppendFormat(CultureInfo.InvariantCulture, "<ModifyMeetingRequest>");
            sb.AppendFormat(CultureInfo.InvariantCulture, "<StringQuery fieldName=\"mid\" operator=\"=\" value=\"{0}\"/><OptionList>", updated.Mid);

            if (updated.EndTime != orig.EndTime)
                sb.AppendFormat(CultureInfo.InvariantCulture, "<TimeOption name=\"endTime\" value=\"{0}\"/>", ToUTCZulu(updated.EndTime));

            if (updated.AudiencePassword != orig.AudiencePassword)
                sb.AppendFormat(CultureInfo.InvariantCulture, "<StringOption name=\"audiencePassword\" value=\"{0}\"/>", updated.AudiencePassword);

            if (updated.MaxUsers != orig.MaxUsers)
                sb.AppendFormat(CultureInfo.InvariantCulture, "<DecimalOption name=\"maxUsers\" value=\"{0}\"/>", updated.MaxUsers);

            if (updated.Name != orig.Name)
                sb.AppendFormat(CultureInfo.InvariantCulture, "<StringOption name=\"name\" value=\"{0}\"/>", updated.Name);

            if (updated.Title != orig.Title)
                sb.AppendFormat(CultureInfo.InvariantCulture, "<StringOption name=\"title\" value=\"{0}\"/>", updated.Title);

            if (updated.Owner != orig.Owner)
                sb.AppendFormat(CultureInfo.InvariantCulture, "<StringOption name=\"owner\" value=\"{0}\"/>", updated.Owner);

            if (updated.PresenterPassword != orig.PresenterPassword)
                sb.AppendFormat(CultureInfo.InvariantCulture, "<StringOption name=\"presenterPassword\" value=\"{0}\"/>", updated.PresenterPassword);

            if (updated.StartTime != orig.StartTime)
                sb.AppendFormat(CultureInfo.InvariantCulture, "<TimeOption name=\"startTime\" value=\"{0}\"/>", ToUTCZulu(updated.StartTime));

            if (updated.Status != orig.Status)
                sb.AppendFormat(CultureInfo.InvariantCulture, "<StringOption name=\"status\" value=\"{0}\"/>", updated.Status.ToString());

            sb.AppendFormat(CultureInfo.InvariantCulture, "</OptionList><FieldList><Name>mid</Name></FieldList></ModifyMeetingRequest>");

            return ToXmlDocument(string.Format(CultureInfo.InvariantCulture, AuthWrapper, sb.ToString()));
        }

        /// <summary>
        /// Create a new meeting
        /// </summary>
        /// <param name="meetingName">Unique name for meeting</param>
        /// <param name="meetingTitle">Title fo meeting</param>
        /// <param name="meetingStartTime">Meeting start time</param>
        /// <param name="meetingEndTime">Meeting end time</param>
        /// <param name="lockDownMembers">Use a specific list of users</param>
        /// <param name="audienceMembers">List of audience members</param>
        /// <returns>Returns xml string request</returns>
        private XmlDocument CreateMeetingHelper(string meetingName, string meetingTitle, DateTime meetingStartTime, DateTime meetingEndTime, bool lockDownMembers, string[] audienceMembers, int maxUsers)
        {
            StringBuilder audience = new StringBuilder();

            if (lockDownMembers)
            {
                audience.Append("<StringListOption name=\"audienceList\">");

                foreach (string s in audienceMembers)
                    audience.AppendFormat(CultureInfo.InvariantCulture, "<String>{0}</String>", s);

                audience.Append("</StringListOption>");
            }

            StringBuilder request = new StringBuilder();

            request.AppendFormat(CultureInfo.InvariantCulture, "<CreateMeetingRequest maxUsers=\"{0}\" name=\"{1}\" title=\"{2}\">", maxUsers, meetingName, meetingTitle);

            request.AppendFormat(CultureInfo.InvariantCulture, "<OptionList><TimeOption name=\"startTime\" value=\"{0}\"/><TimeOption name=\"endTime\" value=\"{1}\"/>{2}</OptionList>",
                ToUTCZulu(meetingStartTime),
                ToUTCZulu(meetingEndTime),
                audience.ToString());

            request.AppendFormat(CultureInfo.InvariantCulture, "<FieldList><Name>mid</Name><Name>audiencePassword</Name><Name>presenterPassword</Name></FieldList></CreateMeetingRequest>");

            return ToXmlDocument(string.Format(CultureInfo.InvariantCulture, AuthWrapper, request.ToString()));
        }

        private LiveMeetingUser CreateNewUserhelper(string userName, string password, string firstName, string lastName, string email, bool sendWelcomeMessage)
        {
            XmlDocument response = HttpRequestHelper.PostXmlMessageRequest(CreateUser(userName, password, firstName, lastName, email, sendWelcomeMessage), PostingUrl) as XmlDocument;

            LiveMeetingUser user = GetUser(userName);
            user.Password = string.IsNullOrEmpty(password) ? 
                response.SelectSingleNode("PlaceWareConfCenter/CreateUserReply").SelectSingleNode("@password").Value : 
                password;

            return user;
        }
        
        private static string ExtractSecurityContextTicket(XmlDocument response)
        {
            return response.SelectSingleNode("PlaceWareConfCenter/CreateTicketReply").Attributes["ticket"].ToString();
        }

        private XmlDocument GetSecurityContextTicketHelper(string user, int duration)
        {
            Contract.Requires(!string.IsNullOrEmpty(this.AuthWrapper));

            return ToXmlDocument(string.Format(CultureInfo.InvariantCulture, AuthWrapper,
                string.Format(CultureInfo.InvariantCulture, "<CreateTicketRequest duration=\"{0}\"><GeneralLoginContext userID=\"{1}\"/></CreateTicketRequest>", duration, user)));
        }

        /// <summary>
        /// Create a new user
        /// </summary>
        /// <param name="username">The username requested</param>
        /// <param name="password">The user's requested password</param>
        /// <param name="firstName">The user's first name</param>
        /// <param name="lastName">The user's last name</param>
        /// <param name="email">The user's email address</param>
        /// <returns>Returns an xml string request</returns>
        private XmlDocument CreateUser(string username, string password, string firstName, string lastName, string email, bool sendWelcomeMessage)
        {
            StringBuilder request = new StringBuilder();

            if (string.IsNullOrEmpty(password))
                request.AppendFormat(CultureInfo.InvariantCulture, "<CreateUserRequest userID=\"{0}\" sendWelcomeMessage=\"{1}\">", username, sendWelcomeMessage);
            else
                request.AppendFormat(CultureInfo.InvariantCulture, "<CreateUserRequest userID=\"{0}\" password=\"{1}\" sendWelcomeMessage=\"{2}\">", username, password, sendWelcomeMessage);

            request.AppendFormat(CultureInfo.InvariantCulture, "<OptionList><StringOption name=\"email\" value=\"{0}\" />", email);
            request.AppendFormat(CultureInfo.InvariantCulture, "<StringOption name=\"lastName\" value=\"{0}\" />", lastName);
            request.AppendFormat(CultureInfo.InvariantCulture, "<StringOption name=\"firstName\" value=\"{0}\" /></OptionList>", firstName);
            request.Append("</CreateUserRequest>");

            return ToXmlDocument(string.Format(CultureInfo.InvariantCulture, AuthWrapper, request.ToString()));
        }

        private static bool parseUploadResponse(XmlDocument response, byte[] payload)
        {
            return parseTransferReply(
                HttpRequestHelper.GetXmlResponseFromStream(
                HttpRequestHelper.PostBytes(payload, new Uri(response.SelectSingleNode("PlaceWareConfCenter/GetURLReply").Attributes["url"].Value))) as XmlDocument);
        }

        private static bool parseTransferReply(XmlDocument response)
        {
            return response.SelectSingleNode("PlaceWareConfCenter/MassTransferReply/InfoWarnFaultList/InfoWarn/Code").InnerText.Contains("Info.Server.Success");
        }

        private XmlDocument PostUploadRequest(string meetingName, string fileName, int payloadLength, ResourceType type)
        {
            StringBuilder sb = new StringBuilder();

            sb.AppendFormat("<GetURLRequest><StringQuery fieldName=\"name\" operator=\"=\" value=\"{0}\"/><OptionList>", meetingName);
            sb.AppendFormat("<DecimalOption name=\"bytes\" value=\"{0}\"/><BooleanOption name=\"isUpload\" value=\"True\"/>", payloadLength);
            sb.AppendFormat("<EnumerationOption name=\"resourceType\" value=\"{0}\">", type.ToString());

            foreach (var r in Enum.GetNames(typeof(ResourceType)))
                sb.AppendFormat("<String>{0}</String>", r);

            sb.Append("</EnumerationOption>");
            sb.AppendFormat("<BooleanOption name=\"showSlideSet\" value=\"True\"/><StringOption name=\"uploadName\" value=\"{0}\"/></OptionList></GetURLRequest>", fileName);

            return HttpRequestHelper.PostXmlMessageRequest(ToXmlDocument(string.Format(CultureInfo.InvariantCulture, AuthWrapper, sb.ToString())), PostingUrl) as XmlDocument;
        }

        private XmlDocument CreateGroupHelper(string groupName, string[] users)
        {
            StringBuilder sb = new StringBuilder();

            sb.AppendFormat(CultureInfo.InvariantCulture, "<CreateGroupRequest groupID=\"{0}\"><UserList>", groupName);

            foreach (var u in users)
                sb.AppendFormat(CultureInfo.InvariantCulture, "<U>{0}</U>", u);

            sb.AppendFormat(CultureInfo.InvariantCulture, "</UserList></CreateGroupRequest>");

            return ToXmlDocument(string.Format(CultureInfo.InvariantCulture, AuthWrapper, sb.ToString()));
        }

        private XmlDocument DeleteGroupHelper(string groupName)
        {
            Contract.Requires(!string.IsNullOrEmpty(this.AuthWrapper));

            return ToXmlDocument(string.Format(CultureInfo.InvariantCulture, AuthWrapper, string.Format(CultureInfo.InvariantCulture, "<DeleteGroupRequest groupID=\"{0}\"/>", groupName)));
        }

        private static XmlDocument ToXmlDocument(string xml)
        {
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(xml);

            return doc;
        }

        /// <summary>
        /// Get the UTC Zulu time of a datetime
        /// </summary>
        /// <param name="dt">The time</param>
        /// <returns>Returns a datetime string in Zulu</returns>
        private static string ToUTCZulu(DateTime dt)
        {
            return dt.ToUniversalTime().ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'", CultureInfo.InvariantCulture);
        }

        /// <summary>
        /// Creates an XML header with authentication
        /// </summary>
        /// <returns>Returns XML Header</returns>
        private static string CreateAuthWrapper(string apiUser, string apiPassword)
        {
            Contract.Ensures(!string.IsNullOrEmpty(Contract.Result<string>()));

            StringBuilder wrapper = new StringBuilder();

            // TODO: need to encode
            wrapper.AppendFormat(CultureInfo.InvariantCulture, "<PlaceWareConfCenter authUser=\"{0}\" authPassword=\"{1}\">", SecurityElement.Escape(apiUser), SecurityElement.Escape(apiPassword));
            wrapper.Append("{0}");
            wrapper.Append("</PlaceWareConfCenter>");

            return wrapper.ToString();
        }
    }
}